/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * fault.cc																	*
 *																			*
 * Fault handlers, for such things as GPFs									*
 *																			*
\***************************************************************************/

#include <console.h>
#include <system.h>
#include <stdio.h>
#include <winman.h>
#include <gui.h>
#include <task.h>
#include <paging.h>
#include <syscall.h>

/// Sets an idt entry
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags);

/// Fault handler
extern "C" void fault_handler(struct regs *r);

/* These are function prototypes for all of the exception
*  handlers: The first 32 entries in the IDT are reserved
*  by Intel, and are designed to service exceptions! */
extern "C" {
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void isr128(); /// Our SysCall, on int 0x80
};

/// Sets up the 32 required IDT entries and installs the syscall interrupt
void isrs_install()
{
    idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
    idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
    idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
    idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
    idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
    idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
    idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
    idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);

    idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
    idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
    idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
    idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
    idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
    idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
    idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
    idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);

    idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
    idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
    idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
    idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
    idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
    idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
    idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
    idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);

    idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
    idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
    idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
    idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
    idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
    idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
    idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
    idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
	
	// setup the syscall
    idt_set_gate(128, (unsigned)isr128, 0x08, 0xEE);
}

/// Exception messages
const char *exception_messages[] =
{
    "Division By Zero",
    "Debug",
    "Non Maskable Interrupt",
    "Breakpoint",
    "Into Detected Overflow",
    "Out of Bounds",
    "Invalid Opcode",
    "No Coprocessor",

    "Double Fault",
    "Coprocessor Segment Overrun",
    "Bad TSS",
    "Segment Not Present",
    "Stack Fault",
    "General Protection Fault",
    "Page Fault",
    "Unknown Interrupt",

    "Coprocessor Fault",
    "Alignment Check",
    "Machine Check",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",

    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved"
};

/// Handles any fault in the operating system
void fault_handler(struct regs *r)
{
	// check if it's a GPF and we're in V8086 mode
	if( r->eflags & 0x20000 && r->int_no == 13 )
	{		
		// the IVT
		uint16_t* ivt = (uint16_t*) 0;
		
		// the stack, in 16-bit and 32-bit
		uint16_t* stack = (uint16_t*) FP_TO_LINEAR( 0x20, r->useresp );
		uint32_t* stack32 = (uint32_t*) stack;
		
		// instruction pointer
		uint8_t* ip = (uint8_t*) FP_TO_LINEAR( r->cs, r->eip );
		
		// index of execution
		uint32_t exc = 0;
		
		// operand and address sizes
		bool Op32 = false, Addr32 = false;
		
		// safe op?
		bool SafeOp = true;
		
		// knock out any extra stuff at the front
		while( SafeOp )
		{
			switch( ip[0] )
			{
				// 32-bit operand
				case 0x66:
				
					// we are using 32-bit operands
					Op32 = true;
					
					// increment the IP
					ip++;
					
					// set the new EIP in the stack
					r->eip++;
					
					break;
				
				// 32-bit address
				case 0x67:
				
					// 32-bit addresses
					Addr32 = true;
					
					// increment the IP
					ip++;
					
					// set the new EIP in the stack
					r->eip++;
					
					break;

				// PUSHF
				case 0x9C:
				
					// check for 32-bit
					if( Op32 )
					{
						// decrement the stack by 4 bytes
						r->useresp = ( ( r->useresp & 0xFFFF ) - 4 ) & 0xFFFF;
						
						// decrement the stack
						stack32--;
						
						// put the flags on the stack
						stack32[0] = r->eflags & 0xDFF;
						
						// see if we have interrupts enabled
						if( r->eflags & 0x200 )
							stack32[0] |= 0x200; // enable in flags
						else
							stack32[0] ^= 0x200; // disable
					}
					else
					{
						// decrement the stack by 2 bytes
						r->useresp = ( ( r->useresp & 0xFFFF ) - 2 ) & 0xFFFF;
						
						// decrement the stack
						stack--;
						
						// put the flags on the stack
						stack[0] = r->eflags;
						
						// see if we have interrupts enabled
						if( r->eflags & 0x200 )
							stack[0] |= 0x200; // enable in flags
						else
							stack[0] ^= 0x200; // disable
					}
						
					// increment eip
					r->eip++;
					
					// go back to task
					return;
				
				// POPF
				case 0x9D:
				
					// check for 32-bit
					if( Op32 )
					{
						// set the EFLAGS
						r->eflags = 0x20200 | ( stack32[0] & 0xDDF );
						
						// set the new stack pointer
						r->useresp = ( ( r->useresp & 0xFFFF ) + 4 ) & 0xFFFF;
					}
					else
					{
						// set the EFLAGS
						r->eflags = 0x20200 | stack[0];
						
						// set the new stack pointer
						r->useresp = ( ( r->useresp & 0xFFFF ) + 2 ) & 0xFFFF;
					}
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
				
				// CLI
				case 0xFA:
				
					// take out the interrupt flag
					if( r->eflags & 0x200 )
						r->eflags ^= 0x200;
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
				
				// STI
				case 0xFB:
				
					// put in the interrupt flag
					if( ( r->eflags & 0x200 ) == 0 )
						r->eflags &= 0x200;
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
					
				// 0xCD: INT n
				case 0xCD:
				
					// int80 = kill current task
					if( ip[1] == 0x80 )
					{						
						// set the current task's registers
						SetRegs( currpid(), r );
						
						// kill it
						kill( currpid() );
						
						// reschedule
						resched();
						
						// set the new esp
						// r->esp = *current_task[0].esp;
						
						// increment the eip too
						r->eip += 2;
						
						// return to the new task
						return;
					}
					
					// decrement the stack 3 times
					stack -= 3;
					
					// put the data on the stack
					stack[0] = (uint16_t) ( r->eip + exc + 2 );
					stack[1] = r->cs;
					stack[2] = (uint16_t) r->eflags;
					
					// set the new stack pointer
					r->useresp = ( ( r->useresp & 0xFFFF ) - 6 ) & 0xFFFF;
					
					// set eflags
					if( r->eflags & 0x200 )
						r->eflags ^= 0x200;
					
					// jump to the necessary location
					r->cs = ivt[ ip[exc+1] * 2 + 1 ];
					r->eip = ivt[ ip[exc+1] * 2 ];
					
					// return, this will take us to the interrupt handler
					return;
				
				// IRET
				case 0xCF:
				
					// set EFLAGS, CS and EIP, then refresh Ring3ESP
					r->eip = stack[0];
					r->cs = stack[1];
					r->eflags = stack[2] | 0x20200;
					
					// new stack pointer
					r->useresp = ( ( r->useresp & 0xFFFF ) + 6 ) & 0xFFFF;
					
					// return to the new place
					return;

				default:
				
					// oops!
					kprintf( "Unknown opcode: 0x%x!\n", ip[0] );
					
					// break free from the loop
					SafeOp = false;
			}
		}
		
		// loop forever - dud op
		while( true );
	}
	
	// syscall?
	if( r->int_no == 0x80 )
	{
		// run the syscall
		SysCall( r );
	}
	else
	{
		// tell me info
		kprintf( "\nI've been rudely interrupted by the processor with this message for you:\n%s\n", exception_messages[r->int_no] );
		
		// print the registers
		kprintf( "Crash at 0x%8x\tEFLAGS: 0x%8x\t\t Process: %d\n", r->eip, r->eflags, currpid() );
/*		kprintf( "CPU state at time of crash:\n" );
		if( r->int_no == 14 )
		{
			union errcodedat {
				struct {
					unsigned int present : 1;
					unsigned int rw : 1;
					unsigned int priv : 1;
					unsigned int rsvdbit : 1;
					unsigned int instfetch : 1;
					unsigned int rsvd : 27;
				} errcode;
				unsigned int err_code;
			} myerrcode;
			myerrcode.err_code = r->err_code;
			kprintf( "------------------------------------------\n" );
			kprintf( "PAGE FAULT, error code information:\n" );
			kprintf( "Error because of %s\n", myerrcode.errcode.present ? "a privilege violation" : "an access of a non-present page" );
			kprintf( "Was a %s\n", myerrcode.errcode.rw ? "write" : "read" );
			kprintf( "Processor was in %s mode\n", myerrcode.errcode.priv ? "user" : "supervisor" );
			kprintf( "Reserved bit violation: %s\n", myerrcode.errcode.rsvdbit ? "yes" : "no" );
			kprintf( "%s caused by an instruction fetch\n", myerrcode.errcode.instfetch ? "Was" : "Wasn't" );
			kprintf( "------------------------------------------\n" );
		}
		else
		{
			union errcodedat {
				struct {
					unsigned int ext : 1;
					unsigned int idt : 1;
					unsigned int ti : 1;
					unsigned int sel : 13;
					unsigned int rsvd : 16;
				} __attribute__((packed)) errcode;
				unsigned int err_code;
			} myerrcode;
			myerrcode.err_code = r->err_code;
			kprintf( "Error code: 0x%x [Sel=0x%x, External: %s, Selector is an IDT entry: %s, LDT: %s]\n", r->err_code, myerrcode.errcode.sel, myerrcode.errcode.ext ? "yes" : "no", myerrcode.errcode.idt ? "yes" : "no", myerrcode.errcode.ti ? "yes" : "no" );
		}
		kprintf( "Interrupt number: %d\n", r->int_no );
		kprintf( "EAX: 0x%8x\tEBX: 0x%8x\tECX: 0x%8x\tEDX: 0x%8x\n", r->eax, r->ebx, r->ecx, r->edx );
		kprintf( "EBP: 0x%8x\tESP: 0x%8x\tESI: 0x%8x\tEDI: 0x%8x\n", r->ebp, r->esp, r->esi, r->edi );
		if( r->eflags & 0x20000 )
		{
			kprintf( "V8086 SS:ESP\n" );
			kprintf( "SS: 0x%8x\tESP: 0x%8x\n", r->ss, r->useresp );
		}
		kprintf( "Segments at time of crash:\n" );
		kprintf( "CS: 0x%8x\tDS: 0x%8x\tES: 0x%8x\nFS: 0x%8x\tGS: 0x%8x\tSS: 0x%8x\n", r->cs, r->ds, r->es, r->fs, r->gs, r->ss );
		kprintf( "Control registers:\n" );
		kprintf( "CR0: 0x%8x\tCR2: 0x%8x\tCR3: 0x%8x\n", read_cr0(), read_cr2(), read_cr3() );*/

		while( 1 );
		
		// was it a user process?
		if( currpid() > 0 )
		{
			// yield its control
			ResetTimeslice( currpid() );
			
			// we are allowed to kill it
			kill( currpid() );
			
			// load a new task
			resched();
		}
		// Just by the architecture of the OS, I doubt that we'll ever crash like this...
		else
		{
			// loop forever, we've crashed in the kernel
			while( true );
		}
	}
}
